package com.game.stepbystep;

import java.util.Vector;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.m2g.SVGEventListener;
import javax.microedition.m2g.SVGImage;
import org.netbeans.microedition.svg.SVGAnimatorWrapper;
import org.netbeans.microedition.svg.SVGMenu;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;
import org.w3c.dom.events.EventTarget;
import org.w3c.dom.svg.SVGAnimationElement;
import org.w3c.dom.svg.SVGElement;

/**
 * This code is copy-pasted from TouchSVGMenu class definition because it does not allow inheritance due to private class members
 */
public class TouchSVGMenu extends SVGAnimatorWrapper implements EventListener{
    
    
    private static final String FOCUSINANIM_PREFFIX = "focusInAnim_";
    private static final String FOCUSOUTANIM_PREFIX = "focusOutAnim_";
    
    /**
     * Command fired when the user chooses a menu element.
     */
    public static final Command SELECT_COMMAND = new Command("SELECT_COMMAND",Command.ITEM,0); // NOI18N
    
    /**
     * value of selected index when no menu item is selected
     */
    public static final int SELECTED_INDEX_NONE = -1;
    
    
    private static final int NO_FOCUS = -1;
    
    private Vector menuElements;
    private Document svgDocument;
    private int currentFocus = NO_FOCUS;
    private Display display;
    private final int ERR_NOTFOUND = -1;
    
    // menu element structure
    private static class MenuElement {
        SVGElement focusableElement;
        int gameActionId;
        SVGAnimationElement focusInAnimation;
        SVGAnimationElement focusOutAnimation;
    }
    
    /**
     * Creates a new instance of TouchSVGMenu. It requires SVGImage to be displayed as
     * menu and display.
     * 
     * <p/> Please note, supplied SVGImage shouldn't be reused in other SVGAnimator.
     */
    public TouchSVGMenu(SVGImage svgImage, Display display) {
        super(svgImage, display);
        this.svgDocument = svgImage.getDocument();
        this.menuElements = new Vector();
        this.display = display;
        setResetAnimationWhenStopped(false); // animation cannot be reset automatically        
        setSVGEventListener(new TouchSVGMenu.SvgMenuEventListener()); // set menu listener        
        setFullScreenMode(true); // menu is usually full screen
    }
    
    
    /**
     * Adds a menu element to the menu component. The menu element is identified
     * by SVG element ID, so when the menu element is being selected, the
     * element with given ID is being focused.
     */
    public void addMenuElement(String svgElementId) {
        this.addMenuElement(svgElementId,Canvas.FIRE);
    }
    
    
    /**
     * Gets menu element id for given index.
     * @return string element ID
     * @throws IndexOutOfBoundException when a wrong index is used
     */
    public String getMenuElementID(int index) throws IndexOutOfBoundsException {
       if ((index < 0) || (index >= menuElements.size())) throw new IndexOutOfBoundsException("Wrong index for menu element: "+index);
       return ((TouchSVGMenu.MenuElement)menuElements.elementAt(index)).focusableElement.getId();
    }
    
    
    /**
     * Implementation of addding the menu element
     */
    private void addMenuElement(String svgElementId, int gameActionId) {
        TouchSVGMenu.MenuElement element = new TouchSVGMenu.MenuElement();
        element.focusableElement = (SVGElement) svgDocument.getElementById(svgElementId);
        element.gameActionId = gameActionId;
        // now try to find animation elements in by our patterns (Nokia S40 workaround)
        // focus.in animation replacement
        Element animElement = svgDocument.getElementById(FOCUSINANIM_PREFFIX + svgElementId);
        if (animElement instanceof SVGAnimationElement) {
            element.focusInAnimation = (SVGAnimationElement)animElement;
        }
        // focus.out animation replacement
        animElement = svgDocument.getElementById(FOCUSOUTANIM_PREFIX + svgElementId);
        if (animElement instanceof SVGAnimationElement) {
            element.focusOutAnimation = (SVGAnimationElement)animElement;
        }
        
        EventTarget eventTarget = (EventTarget)(element.focusableElement);
        eventTarget.addEventListener("click", this, false);
        
        menuElements.addElement(element);
        resetFocus();
    }

    public void handleEvent(Event evt) {
        int idx = touchFindElementIndex(evt.getCurrentTarget().toString());
        if(idx == ERR_NOTFOUND){
            return;
        }
        if(currentFocus == NO_FOCUS){
            currentFocus = idx;
            focusOn(currentFocus,0);
        }else{
            focusOn(currentFocus,idx);
            currentFocus = idx;
        }
        
        final CommandListener commandListener = getCommandListener();
        if (commandListener != null) {
            commandListener.commandAction(TouchSVGMenu.SELECT_COMMAND,this);
        }
    }


    private int touchFindElementIndex(String elemStr) {
        for(int i = 0; i < menuElements.size(); i++){
            TouchSVGMenu.MenuElement element = (TouchSVGMenu.MenuElement) menuElements.elementAt(i);
            if(element.focusableElement.toString().equals(elemStr)){
                return i;
            }
        }
        return ERR_NOTFOUND;
    }
    
    /**
     * Gets index of selected menu element
     */
    public int getSelectedIndex() {
        return currentFocus;
    }
    
    // we should also think about removeMenuElement, but this is not necessary for Visual Designer
    
    
    private void focusOn(int currentFocus, int nextFocus) {
        //System.out.println("current="+currentFocus+", next="+nextFocus);
        TouchSVGMenu.MenuElement currentElement = getMenuElementAt(currentFocus);
        TouchSVGMenu.MenuElement nextElement = getMenuElementAt(nextFocus);
        if (currentElement != null) {
            // if it has focus out animation replacement
            if (currentElement.focusInAnimation != null) {
                stopAnimation(currentElement.focusInAnimation);
                //System.out.println("Stopping focusIn replacement for current");
            }
            if (currentElement.focusOutAnimation != null) {
                startAnimation(currentElement.focusOutAnimation);
                //System.out.println("Dispatching focusOut replacement for current");
            }
        }
        if (nextElement != null) {            
            if (nextElement.focusOutAnimation != null) {
                stopAnimation(nextElement.focusOutAnimation);
                //System.out.println("Stopping focusOut replacement for cunextrrent");
            }
            if (nextElement.focusInAnimation != null) {
                // if it has focus in animation replacement
                // remove current focus
                getSVGImage().focusOn(null); 
                // start focus animation replacement
                startAnimation(nextElement.focusInAnimation);
                //System.out.println("Dispatching focusIn replacement for next");
            } else {
                // else use delvering focus on element
                getSVGImage().focusOn(nextElement.focusableElement);
                //System.out.println("Dispatching regular focusIn");
            }
        } else {
            // we have a problem houston -- remove focus
            getSVGImage().focusOn(null);
            //System.out.println("Dispatching regular focusIn on null");
        }
    }
    
    
    
    private TouchSVGMenu.MenuElement getMenuElementAt(int index) {
        if ((index >= 0) && (index < menuElements.size())) {
            return (TouchSVGMenu.MenuElement)menuElements.elementAt(index);
        } else {
            return null;
        }
    }
    
    private void startAnimation(final SVGAnimationElement svgAnimationElement) {
        Runnable runnable = new Runnable() {
           public void run() {
               svgAnimationElement.beginElementAt(0f);
           }
        };
        invokeLaterSafely(runnable);
    }
    
    private void stopAnimation(final SVGAnimationElement svgAnimationElement) {
        Runnable runnable = new Runnable() {
           public void run() {
               svgAnimationElement.endElementAt(0f);
           }
        };
        invokeLaterSafely(runnable);
    }
    
    
    /**
     * focus on the first element if applicable;
     */
    public void resetFocus() {
        focusOn(currentFocus,0);
        currentFocus = 0;
    }
    
    
    /**
     * By default, focusNext simply moves the focus on the
     * next focusable element in the focus ring.
     */
    public void focusNext() {
        int nextFocus = currentFocus;
        if (nextFocus + 1 != menuElements.size()) {
            nextFocus++;
        } else {
            nextFocus = 0;
        }
        focusOn(currentFocus,nextFocus);
        currentFocus = nextFocus;
    }
    
    /**
     * By default, focusNext simply moves the focus on the
     * previous focusable element in the focus ring.
     */
    public void focusPrev() {
        int nextFocus = currentFocus;
        if (nextFocus == 0) {
            nextFocus = menuElements.size();
        }
        nextFocus--;
        focusOn(currentFocus,nextFocus);
        currentFocus = nextFocus;
    }
    
    
    
    
    // Listen to MIDP key events
    // - UP/LEFT    -> Focus on previous item.
    // - DOWN/RIGHT -> Focus on next item.
    private class SvgMenuEventListener implements  SVGEventListener {
        public void keyPressed(int keyCode) {
            int gameAction = getSvgCanvas().getGameAction(keyCode);
            if (gameAction == Canvas.UP
                    ||
                    gameAction == Canvas.LEFT) {
                getAnimator().invokeLater(new Runnable() {
                    public void run() {
                        focusPrev();
                    }
                });
            } else if (gameAction == Canvas.DOWN
                    ||
                    gameAction == Canvas.RIGHT) {
                getAnimator().invokeLater(new Runnable() {
                    public void run() {
                        focusNext();
                    }
                });
            }
            
            TouchSVGMenu.MenuElement currentMenuElement = getMenuElementAt(currentFocus);
            if (currentMenuElement != null) {
                if (gameAction == currentMenuElement.gameActionId){
                    // fire the action
                    fireSelectCommandAction();
                }
                // else do nothing !!!                
            } else {
                //System.out.println("SvgMenuEventListener.keyPressed: Houston we have a problem : currentMenuElement == null !!!");
            }
        }
        
        
        private void fireSelectCommandAction() {
            final CommandListener commandListener = TouchSVGMenu.this.getCommandListener();
            if (commandListener != null) {
                commandListener.commandAction(TouchSVGMenu.SELECT_COMMAND,TouchSVGMenu.this);
            }
        }
        
        public void keyReleased(int keyCode) {
        }
        
        public void pointerPressed(int x, int y) {
        }
        
        public void pointerReleased(int x, int y) {
        }
        
        public void hideNotify() {
        }
        
        public void showNotify() {
        }
        
        public void sizeChanged(int width, int height) {
        }
    }
    
   
}
